package org.proxywars.engine.ui;

import java.awt.Font;
import java.awt.Insets;

import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.system.DisplaySystem;
import com.jmex.font2d.Font2D;
import com.jmex.font2d.Text2D;

/**
 * So far I have tried to implement a text label using Text2D...
 * Since Text2D is still unfinished, this class is not really functional yet.
 * 
 * @author Steven DeVries
 */
public class TextLabel extends Node {

    private static final long serialVersionUID = 1L;
    
    /* Constant Variables */
    /** Alignment constant: used to determine how to align the text. */
    public static final int ALIGN_LEFT = 0;
    /** Alignment constant: used to determine how to align the text. */
    public static final int ALIGN_RIGHT = 1;
    /** Alignment constant: used to determine how to align the text. */
    public static final int ALIGN_TOP =2;
    /** Alignment constant: used to determine how to align the text. */
    public static final int ALIGN_BOTTOM = 3;
    /** Alignment constant: used to determine how to align the text. */
    public static final int ALIGN_CENTER = 4;
    
    /** Direction constant: used to determine how to print the text. */
    public static final int TEXT_HORIZONTAL = 0;
    /** Direction constant: used to determine how to print the text. */
    public static final int TEXT_VERTICAL = 1;
    
    /** Font Style Constant: used to make text plain. */
    public static final int FONT_PLAIN = 0;
    /** Font Style Constant: used to make text bold. */
    public static final int FONT_BOLD = 1;
    /** Font Style Constant: used to make text italicized. */
    public static final int FONT_ITALIC = 2;
    
    
    /* Protected Variables */
    protected float width;      // float since Rectangle3d uses them.
    protected float height;
    protected Vector2f origin = new Vector2f(0f, 0f);  // same here
    
    
    /* Private Variables */
    private int orientation = TEXT_HORIZONTAL;   // is the text horizontal or vertical.
    
    private int alignHorizontal = ALIGN_LEFT;       // Horizontal Alignment of text
    private int alignVertical = ALIGN_CENTER;      // Vertical Alignment of text
    
    private Insets insets = new Insets(0,0,0,0); // padding around text.
    private Text2D text = new Text2D(new Font2D(), "", 12, Font.PLAIN );
    
    /* Constructors */
    /**
     * Convenience constructor for child classes.
     * Should only ever be used on components that load a skin.
     */
    public TextLabel() {
        
    }
    
    /**
     * Creates a label with the specified location and size.
     * 
     * @param name     Name of the label.
     * @param text       text on the label.
     */
    public TextLabel(String name, String text) {
        this.text.setText(text); // set text so we can get width/height of text.
        initialize(name, 0, 0, (int)this.text.getWidth(), (int)this.text.getHeight(), text);
    }
    
    /**
     * Creates a label with the specified location and size.
     * 
     * @param name     Name of the label.
     * @param x           x location of top left corner of the label.
     * @param y            y location of the top left corner of the label.
     * @param width     width of the label.
     * @param height    height of the label.
     * @param text       text on the label.
     */
    public TextLabel(String name, int x, int y, int width, int height, String text) {
        initialize(name, x, y, width, height, text);        
    }
    
    /**
     * Convenience function so that all the constructors just call one function.
     * Makes it easier to change/update or for children to initialize all the values as well.
     */
    protected void initialize(String name, int x, int y, int width, int height, String text) {
        setName(name);
        origin.x = x;
        origin.y = y;
        this.width = width;
        this.height = height;        
        setText(text);    // also updates  the text - location, alignment, etc.
        
        attachChild(this.text);
        updateRenderState();
    }
    
    
    /* Get Methods */
    /**
     * Returns the horizontal alignment constant for this label.
     * @return either ALIGN_CENTER, ALIGN_LEFT, or ALIGN_RIGHT.
     */
    public int getAlignX() {
        return alignHorizontal;
    }
    
    /**
     * Returns the vertical alignment constant for this label.
     * @return either ALIGN_CENTER, ALIGN_BOTTOM, or ALIGN_TOP.
     */
    public int getAlignY() {
        return alignVertical;
    }
    
    /**
     * Get the height of the label.
     * @return  the height of the label.
     */
    public float getHeight() {
        return height;
    }
    
    /**
     * Get the insets used when painting text.
     * @return  the insets used for the text.
     */
    public Insets getInsets() {
        return insets;
    }
    
    /**
     * Get the width of the label.
     * @return  the height of the label.
     */
    public float getWidth() {
        return width;
    }
    
    /**
     * Returns the x position of the top left corner of the label 
     * in terms of a traditional (x,y) coordinates as opposed to OpenGL's
     * (u,v) coordinates.
     * 
     * @return the x position of the top left corner.
     */
    public int getX() {
        return (int)origin.x;
    }
    
    /**
     * Returns the y position of the top left corner of the label 
     * in terms of a traditional (x,y) coordinates as opposed to OpenGL's
     * (u,v) coordinates.
     * 
     * @return the y position of the top left corner.
     */    
    public int getY() {
        return (int)origin.y;
    }
    
    
    /* Set Methods */
    /**
     * Sets the alignment of the label.
     * The text is drawn from the aligned sides
     * 
     * @param alignHorizontal    Horizontal alignment constant: ALIGN_CENTER, ALIGN_LEFT, or ALIGN_RIGHT.
     * @param alignVertical        Vertical alignment constant: ALIGN_CENTER, ALIGN_BOTTOM, or ALIGN_TOP.
     */
    public void setAlign(int alignHorizontal, int alignVertical) {
        this.alignHorizontal = alignHorizontal;
        this.alignVertical = alignVertical;
        updateText();
    }
    
    /**
     * Set the insets used to space the text.
     * @param top           height of the top area.
     * @param bottom     height of the bottom area.
     * @param left           width of the left area.
     * @param right         width of the right area.
     */
    public void setInsets(int top, int bottom, int left, int right) {
        insets.top = top;
        insets.bottom = bottom;
        insets.left = left;
        insets.right = right;
        updateText();
    }
    
    /**
     * Set the insets used to space the text.
     * @param insets    insets used for slicing.
     */
    public void setInsets(Insets insets) {
        this.insets = insets;
        updateText();
    }
    
    /**
     * Sets the location of the label on the screen.
     * This function uses traditional (x, y) coordinates, (0,0) being top left corner.
     * The top, left corner of the label will be at the specified point.
     * 
     * @param x     horizontal starting point.
     * @param y     vertical starting point.
     */
    public void setLocation(int x, int y) {
        origin.x = x;
        origin.y = y;
        updateText();        
    }
    
    /**
     * Set the size of the label.
     * @param width     width of the label.
     * @param height    height of the label.
     */
    public void setSize(int width, int height) {
        this.width = width;
        this.height = height;
        updateText();
    }
    
    /**
     * Sets a text string on the bar layer.
     * (e.g. Player or Creature name.)
     * 
     * @param text - A string to be displayed on the bar.
     */
    public void setText(String text) {
        this.text.setText(text);
        updateText();        
    }
    
    /**
     * Sets the color of the font.
     * 
     * @param color color to set the font too.
     */
    public void setTextColor(ColorRGBA color) {
        this.text.setTextColor(color);
    }
    
    /**
     * Set the font of the text.
     * TODO: add better documentation here, still not entirely sure how Font2D works.
     * 
     * @param factory   Font2D...
     */
    public void setTextFont(Font2D factory) {
        this.text = new Text2D(factory, this.text.getText().toString(), this.text.getSize(), this.text.getFlags() );
        updateText();
    }
    
    /**
     * Set the direction the text is printed.
     * @param orientation   direction constant: TEXT_HORIZONTAL or TEXT_VERTICAL.
     */
    public void setTextOrientation(int orientation) {
        this.orientation = orientation;
        
        // TODO modify text string to be horiz or vert.
        if(this.orientation == TEXT_VERTICAL) {
            // check if string has \n characters.
                // if not, add them.
        }
        else {
            // check if string has \n characters.
                // if so, remove them.
        }
        updateText();
    }
    
    /**
     * Set the font size of the text. 12 = 12pt.
     * 
     * FIXME: setSize is not yet functional for bitmap fonts...
     * 
     * @param size      size of the text.
     */
    public void setTextSize(int size) {
        //text.setSize((float) size);
        //updateText();
    }
    
    /**
     * Set the style of the text - plain, bold, italic.
     * 
     * @param style  Style constant: FONT_PLAIN, FONT_BOLD, or FONT_ITALIC.
     */
    public void setTextStyle(int style) {        
        switch(style) {
        case FONT_BOLD:
            this.text = new Text2D((Font2D) text.getFactory(), this.text.getText().toString(), this.text.getSize(), Font.BOLD);
            break;
        case FONT_ITALIC:
            this.text = new Text2D((Font2D) text.getFactory(), this.text.getText().toString(), this.text.getSize(), Font.ITALIC);
            break;
        default:
            this.text = new Text2D((Font2D) text.getFactory(), this.text.getText().toString(), this.text.getSize(), Font.PLAIN);
            break;
        }
        updateText();
    }
    
    
    /* Update Methods */
    /**
     * Run when the text is updated.
     * - Verify text isn't too large for label
     * - Verify location on screen. [insets, alignment, etc.]
     */
    protected void updateText() {
        /* Verify text isn't too large for label */
        // FIXME: code should work, but setSize does not work for Text2D yet.
        /*
        float maxTextWidth = width - insets.left - insets.right;
        float maxTextHeight = height - insets.top - insets.bottom;
        
        while(  text.getWidth() > maxTextWidth || 
                    text.getHeight() > maxTextHeight) {
            float fontSize = text.getSize();
            fontSize--;
            text.setSize(fontSize);
        }
        */
        
        /* Verify location on screen. [insets, alignment, etc.] */
        float screenHeight = (float) DisplaySystem.getDisplaySystem().getHeight();
        float u = 0f;
        float v = 0f;
        
        // TODO handle insets
        switch(alignHorizontal) {
        case ALIGN_LEFT:
            u = origin.x + text.getWidth()/2;
            break;
        case ALIGN_RIGHT:
            u = origin.x + width;
            break;
        default:    // center
            u = origin.x + width/2;
            break;        
        }
        
        switch (alignVertical) {
        case ALIGN_BOTTOM:
            v = screenHeight - (origin.y + text.getHeight());
            break;
        case ALIGN_TOP:
            v = screenHeight - (origin.y + text.getHeight()/2);
            break;
        default:    // center
            v = screenHeight - (origin.y + height/2);
            break;                
        }
        
        /* Update the location */
        text.setLocalTranslation(new Vector3f(u, v, 0) );
        
        updateRenderState();
    }
}
